<template>
  <div :class="classNames">
    <ValueInputerWrap
      :form="form"
      :rules="rules_"
    >
      <template v-if="valueType === 'Date'">
        <DatePicker
          v-model:value="curValue"
          :time-type="timeType"
          :format="timeFormat"
          :component-style="componentStyle"
          :clearable="clearable"
          :readonly="readonly"
        />
      </template>

      <template v-else-if="valueType === 'Boolean'">
        <el-switch
          v-model="curValue"
          :readonly="readonly"
        ></el-switch>
      </template>

      <template v-else-if="valueType === 'Number'">
        <el-input-number
          v-model="curValue"
          size="default"
          :min="min"
          :max="max"
          :controls="controls"
          :controls-position="controlsPosition"
          :readonly="readonly"
          :step="step"
          :stepStrictly="stepStrictly"
        ></el-input-number>
      </template>

      <template v-else-if="valueType === 'List'">
        <el-select
          ref="List"
          v-model="curValue"
          :class="`panel-data-select-${xvueTheme} List`"
          :popper-class="`select-poper-${xvueTheme}`"
          :placeholder="placeholder_"
          size="default"
          :multiple="multiple"
          :allow-create="allowCreate"
          filterable
          :disabled="readonly"
          :style="{width: contentWidth}"
          @change="hideListPopup()"
        >
          <el-option
            v-for="(n, i) in optionValues"
            :key="i"
            :label="n.label ? n.label : n.value"
            :value="n.value"
          ></el-option>
        </el-select>
      </template>

      <template v-else-if="valueType === 'Color'">
        <ColorPicker
          v-model:value="curValue"
          :readonly="readonly"
          :colors="colors"
        />
      </template>

      <template v-else-if="valueType === 'Radio'">
        <el-radio-group
          v-model="curValue"
          :readonly="readonly"
        >
          <el-radio
            v-for="(n, i) in optionValues"
            :key="i"
            :label="n.value"
          >
            {{ n.label }}
          </el-radio>
        </el-radio-group>
      </template>

      <template v-else-if="valueType === 'RadioBtn'">
        <el-radio-group
          v-model="curValue"
          size="small"
        >
          <el-radio-button
            v-for="(n, i) in optionValues"
            :key="i"
            :label="n.value"
            :disabled="readonly"
          >{{
            n.label
          }}</el-radio-button>
        </el-radio-group>
      </template>

      <template v-else-if="valueType === 'CheckboxGroup'">
        <el-checkbox-group
          v-model="curValue"
        >
          <el-checkbox
            v-for="(n, i) in optionValues"
            :key="i"
            :label="n.value"
            :disabled="readonly"
          >{{
            n.label
          }}</el-checkbox>
        </el-checkbox-group>
      </template>

      <div
        v-else-if="valueType === 'Vector'"
        class="vector_box"
      >
        <div
          v-for="(dim, index) in dimsArr"
          :key="index"
        >
          <label :title="dim.tip">{{ dim.label }}</label>
          <el-input-number
            v-model="curValue[index]"
            controls-position="right"
            :min="min"
            :max="max"
            :step="step"
            :stepStrictly="stepStrictly"
            size="default"
            :readonly="readonly"
          ></el-input-number>
        </div>
      </div>

      <template v-else-if="valueType === 'Range'">
        <Range
          v-model:value="curValue"
          type="input"
          :min="min"
          :max="max"
        />
      </template>

      <template v-else-if="valueType === 'Textarea'">
        <el-input
          ref="Textarea"
          v-model="curValue"
          v-visual-lifecycle="{mounted:resizeTextarea}"
          class="strTextarea"
          :placeholder="placeholder_"
          :clearable="clearable && optionValues.length === 0"
          :style="{width: contentWidth}"
          size="default"
          :readonly="readonly"
          type="textarea"
          autosize
        ></el-input>
      </template>

      <template v-else-if="valueType === 'Input'">
        <el-input
          v-model="curValue"
          class="strInput"
          :placeholder="placeholder_"
          :clearable="clearable && optionValues.length === 0"
          :style="{width: contentWidth}"
          size="default"
          :readonly="readonly"
        ></el-input>
      </template>

      <template v-else>
        <CompRefConfigWrap
          ref="CompRefConfigWrap"
          v-model:value="curValue"
          :codeMirrorHeight="codeMirrorHeight"
          :placeholder="placeholder"
          :componentStyle="componentStyle"
          @mounted="moveDropList"
        >
          <template #rightInner>
            <div id="rightInner"></div>
          </template>
        </CompRefConfigWrap>
      </template>
    </ValueInputerWrap>

    <el-dropdown
      v-if="dropListDisplay"
      ref="dropList"
      trigger="hover"
      :popper-class="`dropdown_${xvueTheme} visualComponents_theme_${componentStyle}`"
    >
      <span class="el-dropdown-link">
        <el-icon><ArrowDown /></el-icon>
      </span>
      <template #dropdown>
        <el-dropdown-menu
          class="value_input_dropdown_menu"
          :class="'value_input_dropdown_menu_' + xvueTheme"
        >
          <el-dropdown-item
            v-for="(n, i) in optionValues"
            :key="i"
            @click="selectOptionVal(n)"
          >
            {{ n.label ? n.label : n.value }}
          </el-dropdown-item>
          <el-dropdown-item
            v-if="enumEditable"
            @click="editOptionValues()"
          >
            <span style="color:#599ef3">
              {{ $demoi18n('panel.valueInputer.setOptionVal') }}
            </span>
          </el-dropdown-item>
        </el-dropdown-menu>
      </template>
    </el-dropdown>

    <el-dialog
      v-if="optionDialogVisible"
      v-model="optionDialogVisible"
      :title="$demoi18n('panel.valueInputer.editOptionVal')"
      :class="`vs_value_inputer_enum_dialog visualComponents_theme_${componentStyle} ${xvueNameSpace}`"
      :close-on-click-modal="false"
      :append-to-body="true"
      :destroy-on-close="true"
      @close="cancelOptionDialog"
    >
      <el-button
        class="add_option_value"
        type="primary"
        size="default"
        icon="CirclePlus"
        @click="addNewOptionValue"
      >
        {{ $demoi18n('panel.valueInputer.addOptionVal') }}
      </el-button>
      <el-table :data="optionValues">
        <el-table-column
          type="index"
          :label="$demoi18n('word.serialNumber')"
          :width="60"
        ></el-table-column>
        <el-table-column
          prop="value"
          :label="$demoi18n('panel.valueInputer.optionVal')"
          :width="150"
        >
          <template #default="scope">
            <el-input
              v-model="scope.row.value"
              :placeholder="$demoi18n('panelEvent.label.eventKey')"
              size="default"
            ></el-input>
          </template>
        </el-table-column>
        <el-table-column
          :label="$demoi18n('word.operation')"
          :width="100"
        >
          <template #default="scope">
            <el-button
              :link="true"
              type="primary"
              icon="Delete"
              size="default"
              :title="$demoi18n('btn.delete')"
              @click="delOptionValue(scope.row)"
            ></el-button>
          </template>
        </el-table-column>
      </el-table>
      <template #footer>
        <span>
          <el-button
            size="default"
            @click="cancelOptionDialog"
          >
            {{ $demoi18n('btn.cancel') }}
          </el-button>
          <el-button
            size="default"
            type="primary"
            @click="saveOptionValues"
          >
            {{ $demoi18n('btn.confirm') }}
          </el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script>
import ColorPicker from '@/components/common/color-picker/color-picker.vue';
import DatePicker from '@/components/common/date-picker/date-picker.vue';
import CompRefConfigWrap from '@/components/common/low-code/comp-ref-config-wrap.vue';
import Range from '@/components/common/range/range.vue';
import { generateRules } from '@/utils/rules.js';
import { bindVisibleChange, isObject } from '@/utils/util.js';
import dayjs from 'dayjs';
import fakeClickOutside from './fakeClickOutside-mixin.js';
import ValueInputerWrap from './value-inputer-wrap.vue';

export default {
  components: {
    DatePicker,
    ValueInputerWrap,
    ColorPicker,
    Range,
    CompRefConfigWrap,
  },
  mixins: [fakeClickOutside],
  props: {
    valueType: {
      type: String,
      default: 'String', // String, Input, Number, Date, Boolean, List, Color, RadioBtn, Vector, CheckboxGroup, Desc
    },

    value: [String, Number, Date, Boolean, Array],
    enumEditable: {

      // 选项枚举是否可编辑
      type: Boolean,
      default: false,
    },
    enumValues: {

      // 选项枚举值
      type: Array,
      default: () => [],
    },
    placeholder: String,

    controls: {
      type: Boolean,
      default: false,
    },
    controlsPosition: String,
    min: {
      type: Number,
      default: -100000000,
    },
    max: {
      type: Number,
      default: 100000000,
    },

    timeType: {
      type: String,
      default: 'date', // date, month, year, week
    },

    timeFormat: String, // 返回时间格式， 如'YYYY-MM-DD HH:mm:ss'
    componentStyle: {
      type: String,
      default: 'light',
    },

    clearable: {
      type: Boolean,
      default: true,
    },
    multiple: {
      type: Boolean,
      default: false,
    },
    allowCreate: {
      type: Boolean,
      default: false,
    },

    contentWidth: String, // 设置内容宽度

    rules: Array,

    // 无值时简单显示红框，无文字信息提示
    required: {
      type: Boolean,
      default: false,
    },

    // 使用基本校验规则，不允许特殊字符串
    baseRuled: {
      type: Boolean,
      default: false,
    },

    // 维度
    dims: {
      type: String,
      default: '',
    },

    // 步长
    step: {
      type: Number,
      default: 1,
    },

    // 数值输入框是否只能输入 step 的倍数
    stepStrictly: {
      type: Boolean,
      default: false,
    },

    // 不可编辑
    readonly: {
      type: Boolean,
      default: false,
    },

    colors: {

      // 预选色
      type: Array,
      default: () => ['#e6e6e6', '#666666', '#26B0FF', '#15BC83', '#C089D9', '#FF943E', '#FE4C56'],
    },

    label: String,

    // 是否允许输入特殊字符
    allowSpecilChar: {
      type: Boolean,
      default: false,
    },

    // 编辑器高度
    codeMirrorHeight: {
      type: String,
      default: '30',
    },
  },
  emits: ['update:value', 'change', 'saveEnumValues'],
  data() {
    return {
      xvueNameSpace: process.env.xvueNameSpace, // 给挂载在body下的元素加上主类名
      xvueTheme: window._adoPremise.uc.theme,
      curValue: this.value,
      optionDialogVisible: false,
      optionValues: [],
      placeholder_: '',
    };
  },
  computed: {
    dropListDisplay() {
      const types = ['Input', 'String', 'Number'];
      return types.includes(this.valueType) && (this.enumEditable || this.optionValues.length > 0);
    },
    form() {
      return {
        curValue: this.curValue,
      };
    },
    rules_() {
      if (this.rules && this.rules.length) {
        return this.rules;
      } else if (this.$parent.$options._componentTag === 'el-form-item') {
        const { $parent } = this;
        if ($parent.rules && $parent.rules.length) {
          return $parent.rules;
        }
      } else {}

      let rules = [];
      const label = this.label || this.$demoi18n('placeholder.inputValue');
      const { required } = this;
      let isAllStr;
      const lowCodeMax = (this.max === 100000000) ? 80 : this.max;
      switch (this.valueType) {
        case 'String': // 使用低代码编辑器，允许一些特殊字符
          rules = generateRules({
            label,
            required,
            specilChar: !this.allowSpecilChar,
            max: lowCodeMax,
            reg: /[<>&"]/,
          });
          break;
        case 'Date':
          rules = generateRules({ label, required, specilChar: !this.allowSpecilChar });
          break;
        case 'List':
          isAllStr = this.optionValues.every(n => typeof n.value === 'string');
          if (isAllStr) {
            rules = generateRules({ label, required });
          }
          break;
        default: break;
      }
      return rules;
    },
    classNames() {
      const arr = [this.componentStyle, this.valueType, 'value-inputer'];
      return arr;
    },
    dimsArr() {
      let arr = this.dims.split(',');
      arr = arr.map(n => {
        const [label, _tip] = n.split('|');
        const tip = _tip ? _tip : label;
        return { label, tip };
      });
      return arr;
    },
  },
  watch: {
    valueType(type) {
      if (type === 'Number') {
        this.curValue = undefined;
      } else {
        this.curValue = '';
      }
      this.initPlaceHolder();
    },
    dropListDisplay() {
      this.initPlaceHolder();
    },
    value(newVal) {
      this.curValue = newVal;
    },
    curValue(newVal) {
      const value = this.fixValue(newVal);
      this.$emit('update:value', value);
      this.$emit('change', value);
    },
    enumValues(newVal, oldVal) {
      this.initOptionValues();
    },
  },
  created() {
    this.initOptionValues();
    this.initPlaceHolder();
  },
  mounted() {
    this.curValue = this.fixValue(this.value);
  },
  beforeUnmount() {
    clearTimeout(this.dropListTimer);
  },
  methods: {
    hideListPopup() { // css编辑器选择diy模式时，下方出现元素选择器，list下拉不消失，兼容修改
      setTimeout(() => {
        this.$refs.List.dropMenuVisible = false;
      }, 20);
    },
    initOptionValues() {
      const arr = this.enumValues.map(n => {
        if (isObject(n)) {
          return {
            label: n.label,
            value: n.value,
          };
        } else {
          return {
            label: n,
            value: n,
          };
        }
      }).filter(n => n.value !== undefined);
      this.optionValues = arr;
    },
    editOptionValues() {
      this.optionDialogVisible = true;
    },
    addNewOptionValue() {
      this.optionValues.push({
        value: '',
      });
    },
    delOptionValue(item) {
      const index = this.optionValues.indexOf(item);
      this.optionValues.splice(index, 1);
    },
    cancelOptionDialog() {
      this.optionDialogVisible = false;
    },
    saveOptionValues() {
      this.optionDialogVisible = false;
      this.$emit('saveEnumValues', this.optionValues);
    },
    initPlaceHolder() {
      if (this.placeholder) {
        this.placeholder_ = this.placeholder;
        return;
      }
      let tip = this.$demoi18n('word.plsInput');
      if (this.dropListDisplay) {
        tip = this.$demoi18n('word.inputOrSelect');
      }
      switch (this.valueType) {
        case 'Date':
          tip = this.$demoi18n('word.selectDate');
          break;
        case 'Number':
          tip = this.$demoi18n('word.plsInputNumVal');
          break;
        case 'List':
          tip = this.$demoi18n('word.plsSelect');
          break;
        default:
          break;
      }
      this.placeholder_ = tip;
    },
    fixValue(value) {
      let _value = value;
      const { valueType } = this;

      if (valueType === 'Date') {
        if (_value) {
          _value = dayjs(_value).format('YYYY-MM-DD HH:mm:ss');
        } else {
          _value = '';
        }
      } else if (valueType === 'Number') {
        if (/^-?\d+(\.\d+)?$/.test(_value)) {
          _value = Number(_value);
        } else {
          _value = undefined;
        }
      } else {}
      return _value;
    },
    selectOptionVal(n) {
      this.curValue = n.value;
    },
    moveDropList() {
      // 修正下拉位置，不然校验错误信息出现时会特别难看
      this.dropListTimer = setTimeout(() => {
        if (this.$refs.CompRefConfigWrap && this.$refs.dropList && this.$el.querySelector('#rightInner')) {
          this.$el.querySelector('#rightInner').appendChild(this.$refs.dropList.$el);
        }
      }, 500);
    },
    resizeTextarea() {
      if (this.$refs.Textarea) {
        bindVisibleChange(this.$el, () => {
          const $Textarea = this.$refs.Textarea;
          $Textarea.resizeTextarea();
        });
      }
    },
  },
};
</script>

<style lang="less" scoped>
.value-inputer {
  display: flex;
  align-items: center;
  width: 100%;
  position: relative;
  overflow: hidden;
  &.Boolean {
    width: auto;
  }
  &.String{
    min-width: 26px;
  }
  .el-dropdown {
    position: absolute;
    right: 0;
    height: 100%;
    top: 0;
    padding: 3px;
    box-sizing: border-box;
    outline:none;
  }
  .el-dropdown-link {
    width: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
    background-color: transparent;
    outline: none;
  }
  :deep(.el-date-editor) {
    width: 100%;
    .el-input__inner {
      padding-right: 5px;
    }
  }
  :deep(.el-input-number) {
    width: 100%;
  }
  :deep(.strInput.el-input--suffix) {
    .el-input__inner {
      min-width: 10px;
    }
    .el-input__suffix{
      position: relative;
      width: 0;
      right: 20px;
    }
  }
  :deep(.strTextarea){
    .el-textarea__inner{
      word-break: break-all;
      white-space: pre-wrap;
    }
  }
  :deep(.el-form-item.is-error) {
    margin-bottom: 0 !important;
    .el-form-item__error {
      text-align: left;
      position: unset;
    }
  }

  :deep(.vector_box) {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    > div {
      display: flex;
      align-items: center;
      width: 46%;
      margin-bottom: 5px;
      label {
        padding-right: 5px;
        min-width: 10px;
        text-align: right;
      }
      .el-input-number {
        &.is-controls-right {
          .el-input__inner {
            padding-left: 0;
            padding-right: 20px;
          }
          [class*='decrease'],
          [class*='increase'] {
            width: 18px;
          }
        }
      }
    }
  }

  :deep(.List){
    .el-input__inner{
      padding-left: 0;
      padding-right: 0;
      font-size: 12px;
    }
    .el-input__suffix-inner{
      width:0;
      .el-icon{
        position: relative;
        right: 10px;
      }
    }
  }

  :deep(.el-select){
    .el-select__tags{
      margin-left: 1px; // 保留一点距离，防止tag背景挡住select的边框
      >span{
        display: contents; // 保持跟vue2一致，不然会多出来一行空行
      }
    }
  }

  &.dark {
    @bgColor: #0d263a;
    @borderColor: #588ba8;
    @textColor: #ffffff;
    :deep(.el-input-number) {
      .el-input__inner {
        border-color: @borderColor;
        color: @textColor;
        background-color: #000c17;
      }
    }
  }
  &.real {
    @bgColor: #191919;
    @borderColor: #588ba8;
    @textColor: #ffffff;
    :deep(.el-input-number) {
      .el-input__inner {
        border-color: @borderColor;
        color: @textColor;
        background-color: #191919;
      }
    }
  }

}
</style>
<style lang="less">
.value_input_dropdown_menu {
  width: fit-content !important;
  max-height: 200px;
  overflow-y: auto;
  overflow-x: hidden;
  .el-dropdown-menu__item {
    width: 100%;
  }
}
</style>
<style lang="less">
.value_input_dropdown_menu_dark {
  background-color: #091a2d;
  border-color: #588ba8;
  .el-dropdown-menu__item {
    background-color: #122B40;
    color: #83B0CA;
  }

  .el-dropdown-menu__item:focus,
  .el-dropdown-menu__item:not(.is-disabled):hover {
    background-color: #102e4f;
    color: #00f0ff;
  }

  :deep(.popper__arrow) {
    border-bottom-color: #588ba8;

    &::after {
      border-bottom-color: #091a2d !important;
    }
  }
}

.value_input_dropdown_menu::-webkit-scrollbar {
  width: 4px !important;
  height: 4px !important;
}
.value_input_dropdown_menu_dark::-webkit-scrollbar-thumb {
  background: #00f0ff;
  border-radius: 2px;
}
.value_input_dropdown_menu_dark::-webkit-scrollbar-track {
  background: #1a405f;
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
  border-radius: 2px;
}
.value_input_dropdown_menu_dark::-webkit-scrollbar-corner {
  background-color: #1a405f;
}
.el-popper.dropdown_dark{
  border: 1px solid #588ba8;
  .el-popper__arrow{
    &::before{
      border: 1px solid #588ba8;
      background-color: #091a2d;
    }
  }
}
</style>
